'''
It is possible to control the DAQ card without nidaqmx. To do so
the ANSI C DAQmx library can be loaded directly using ctypes. Note that
this method adds a lot of data typing headaches.

This is a port to Python of the ANSI C DAQmx example Cont Acq-Int Clk.c
'''

import ctypes
import numpy
import pylab as pl
import time
import fltk

#Cross platform getch
#  See: http://code.activestate.com/recipes/134892/
try :
    import msvcrt 

    getch = msvcrt.getch
except ImportError:
    
    import sys, tty, termios
    def getch() :
        fd = sys.stdin.fileno()
        old_settings = termios.tcgetattr(fd)
        try:
            tty.setraw(sys.stdin.fileno())
            ch = sys.stdin.read(1)
        finally:
            termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
        return ch

nidaq = ctypes.windll.nicaiu # load the DLL
##############################
# Setup some typedefs and constants
# to correspond with values in
# C:\Program Files\National Instruments\NI-DAQ\DAQmx ANSI C Dev\include\NIDAQmx.h
# the typedefs
int32 = ctypes.c_long
uInt32 = ctypes.c_ulong
uInt64 = ctypes.c_ulonglong
float64 = ctypes.c_double
TaskHandle = uInt32
# the constants
DAQmx_Val_Cfg_Default = int32(-1)
DAQmx_Val_Volts = 10348
DAQmx_Val_Rising = 10280
DAQmx_Val_GroupByChannel = 0

DAQmx_Val_FiniteSamps       = 10178 # Finite Samples
DAQmx_Val_ContSamps         = 10123 # Continuous Samples
DAQmx_Val_HWTimedSinglePoint=  12522 # Hardware Timed Single Point

#*** Values for the everyNsamplesEventType parameter of DAQmxRegisterEveryNSamplesEvent ***
DAQmx_Val_Acquired_Into_Buffer      = 1 # Acquired Into Buffer
DAQmx_Val_Transferred_From_Buffer   = 2 # Transferred From Buffer

#*** Values for the Fill Mode parameter of DAQmxReadAnalogF64, DAQmxReadBinaryI16, DAQmxReadBinaryU16, DAQmxReadBinaryI32, DAQmxReadBinaryU32,
#    DAQmxReadDigitalU8, DAQmxReadDigitalU32, DAQmxReadDigitalLines ***
#*** Values for the Data Layout parameter of DAQmxWriteAnalogF64, DAQmxWriteBinaryI16, DAQmxWriteDigitalU8, DAQmxWriteDigitalU32, DAQmxWriteDigitalLines ***
DAQmx_Val_GroupByChannel    = 0 # Group by Channel
DAQmx_Val_GroupByScanNumber = 1 # Group by Scan Number

##############################
def CHK(err):
    """a simple error checking routine"""
    if err < 0:
        buf_size = 100
        buf = ctypes.create_string_buffer('\000' * buf_size)
        nidaq.DAQmxGetErrorString(err,ctypes.byref(buf),buf_size)
        raise RuntimeError('nidaq call failed with error %d: %s'%(err,repr(buf.value)))

#*********************************************/
# Callback Functions
#   See: http://docs.python.org/library/ctypes.html#callback-functions
#*********************************************/
myCnt = 0
# return type, and then arg types
EVERYNFUNC = ctypes.CFUNCTYPE(int32, TaskHandle, int32, uInt32, ctypes.c_void_p)
def EveryNCallback(taskHandle, everyNsamplesEventType, nSamples, callbackData) :
    data = numpy.zeros((1000,),dtype=numpy.float64)
    read = int32()
    
    CHK(nidaq.DAQmxReadAnalogF64(taskHandle,1000,
                float64(10.0),DAQmx_Val_GroupByScanNumber,data.ctypes.data,
                1000,ctypes.byref(read),None));
    if read > 0 :
        global myCnt
        myCnt += 1
        print "%d: Acquired %d samples." % (myCnt,read.value)
	
    return 0
#and the callback function
#  This can not be inlined into the function call!
EveryNCallback_func = EVERYNFUNC(EveryNCallback)

DONEFUNC = ctypes.CFUNCTYPE(int32, TaskHandle, int32, ctypes.c_void_p)
def DoneCallback(taskHandle, status, callbackData) :
    CHK( status )
    return 0;
#and the callback function
DoneCallback_func = DONEFUNC(DoneCallback)


#*********************************************/
# DAQmx Configure Code
#*********************************************/
taskHandle = TaskHandle(0)
CHK(nidaq.DAQmxCreateTask("",ctypes.byref(taskHandle)))

CHK(nidaq.DAQmxCreateAIVoltageChan(taskHandle,"Dev1/ai0","",
                                    DAQmx_Val_Cfg_Default,
                                    float64(-10.0),float64(10.0),
                                    DAQmx_Val_Volts, None));
CHK(nidaq.DAQmxCfgSampClkTiming(taskHandle,"",float64(10000.0),
                                DAQmx_Val_Rising,DAQmx_Val_ContSamps,
                                uInt64(1000)))

CHK(nidaq.DAQmxRegisterEveryNSamplesEvent(taskHandle,
                            DAQmx_Val_Acquired_Into_Buffer,1000,0,
                            EveryNCallback_func,0))
                            
CHK(nidaq.DAQmxRegisterDoneEvent(taskHandle,0,DoneCallback_func,None));

#*********************************************/
# DAQmx Start Code
#*********************************************/
CHK(nidaq.DAQmxStartTask(taskHandle));

print "Acquiring samples continuously. Press Enter to interrupt";
while True :
    x = getch()
    if x :
        break
    else :
        time.sleep(0.1)
